#include "gstdynamictextoverlay.h"
#include <cairo.h>
#include <pango/pangocairo.h>
#include <math.h>

#define PACKAGE "dynamictextoverlay"
#define VERSION "1.0.0"

GST_DEBUG_CATEGORY_STATIC(gst_dynamic_text_debug);
#define GST_CAT_DEFAULT gst_dynamic_text_debug

enum
{
    PROP_0,
    PROP_TEXT,
    PROP_FONT_SIZE,
    PROP_FONT_NAME,
    PROP_X_POSITION,
    PROP_Y_POSITION,
    PROP_SCROLL_SPEED,
    PROP_SCROLL_DIRECTION,
    PROP_COLOR
};

// 动画类型枚举
typedef enum
{
    ANIMATION_SCROLL = 0, // 普通滚动
    ANIMATION_WAVE   = 1, // 波浪效果
    ANIMATION_BOUNCE = 2, // 弹跳效果
    ANIMATION_FADE   = 3  // 淡入淡出
} AnimationType;

static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE(
        "sink",
        GST_PAD_SINK,
        GST_PAD_ALWAYS,
        GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGBA"))
    );

static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE(
        "src",
        GST_PAD_SRC,
        GST_PAD_ALWAYS,
        GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGBA"))
    );

#define gst_dynamic_text_parent_class parent_class
G_DEFINE_TYPE(GstDynamicText, gst_dynamic_text, GST_TYPE_VIDEO_FILTER);

static void gst_dynamic_text_set_property(GObject *object, guint prop_id,
                                          const GValue *value, GParamSpec *pspec)
{
    GstDynamicText *filter = GST_DYNAMIC_TEXT(object);

    switch (prop_id)
    {
        case PROP_TEXT:
            g_free(filter->text);
            filter->text = g_value_dup_string(value);
            break;
        case PROP_FONT_SIZE:
            filter->font_size = g_value_get_int(value);
            break;
        case PROP_FONT_NAME:
            g_free(filter->font_name);
            filter->font_name = g_value_dup_string(value);
            break;
        case PROP_X_POSITION:
            filter->x_position = g_value_get_double(value);
            break;
        case PROP_Y_POSITION:
            filter->y_position = g_value_get_double(value);
            break;
        case PROP_SCROLL_SPEED:
            filter->scroll_speed = g_value_get_double(value);
            break;
        case PROP_SCROLL_DIRECTION:
            filter->scroll_direction = g_value_get_int(value);
            break;
        case PROP_COLOR:
            filter->color = g_value_get_uint(value);
            break;
        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
            break;
    }
}

static void gst_dynamic_text_get_property(GObject *object, guint prop_id,
                                          GValue *value, GParamSpec *pspec)
{
    GstDynamicText *filter = GST_DYNAMIC_TEXT(object);

    switch (prop_id)
    {
        case PROP_TEXT:
            g_value_set_string(value, filter->text);
            break;
        case PROP_FONT_SIZE:
            g_value_set_int(value, filter->font_size);
            break;
        case PROP_FONT_NAME:
            g_value_set_string(value, filter->font_name);
            break;
        case PROP_X_POSITION:
            g_value_set_double(value, filter->x_position);
            break;
        case PROP_Y_POSITION:
            g_value_set_double(value, filter->y_position);
            break;
        case PROP_SCROLL_SPEED:
            g_value_set_double(value, filter->scroll_speed);
            break;
        case PROP_SCROLL_DIRECTION:
            g_value_set_int(value, filter->scroll_direction);
            break;
        case PROP_COLOR:
            g_value_set_uint(value, filter->color);
            break;
        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
            break;
    }
}

static void gst_dynamic_text_finalize(GObject *object)
{
    GstDynamicText *filter = GST_DYNAMIC_TEXT(object);

    g_free(filter->text);
    g_free(filter->font_name);

    G_OBJECT_CLASS(parent_class)->finalize(object);
}

static GstFlowReturn gst_dynamic_text_transform_frame(GstVideoFilter *filter,
                                                      GstVideoFrame *inframe,
                                                      GstVideoFrame *outframe)
{
    GstDynamicText *dtext = GST_DYNAMIC_TEXT(filter);
    cairo_surface_t *surface;
    cairo_t *cr;
    PangoLayout *layout;
    PangoFontDescription *desc;
    gint width, height;
    static gdouble offset = 0.0;
    static gdouble time = 0.0;

    width = GST_VIDEO_FRAME_WIDTH(inframe);
    height = GST_VIDEO_FRAME_HEIGHT(inframe);

    // 创建Cairo表面
    surface = cairo_image_surface_create_for_data(
        GST_VIDEO_FRAME_PLANE_DATA(outframe, 0),
        CAIRO_FORMAT_ARGB32,
        width, height,
        GST_VIDEO_FRAME_PLANE_STRIDE(outframe, 0));

    cr = cairo_create(surface);
    cairo_set_source_surface(cr, cairo_image_surface_create_for_data(
                                 GST_VIDEO_FRAME_PLANE_DATA(inframe, 0),
                                 CAIRO_FORMAT_ARGB32,
                                 width, height,
                                 GST_VIDEO_FRAME_PLANE_STRIDE(inframe, 0)), 0, 0);
    cairo_paint(cr);

    // 创建Pango布局
    layout = pango_cairo_create_layout(cr);
    pango_layout_set_text(layout, dtext->text, -1);

    // 设置字体
    desc = pango_font_description_new();
    pango_font_description_set_family(desc, dtext->font_name);
    pango_font_description_set_size(desc, dtext->font_size * PANGO_SCALE);
    pango_layout_set_font_description(layout, desc);
    pango_font_description_free(desc);

    // 计算文本尺寸
    int text_width, text_height;
    pango_layout_get_pixel_size(layout, &text_width, &text_height);

    // 更新时间
    time += 0.016; // 假设60fps
    if (time > 2 * M_PI)
    {
        time -= 2 * M_PI;
    }

    // 根据动画类型计算位置
    double x_pos = dtext->x_position;
    double y_pos = dtext->y_position;

    offset += dtext->scroll_speed;
    if (dtext->scroll_direction == 0)
    {
        if (offset > width + text_width)
            offset = -text_width;
        x_pos = offset;
    }
    else
    {
        if (offset > height + text_height)
            offset = -text_height;
        y_pos = offset;
    }

    // 设置文本位置和颜色
    cairo_move_to(cr, x_pos, y_pos);

    double a, r, g, b;
    // 使用字节序无关的方式解析
    a = ((dtext->color >> 24) & 0xFF); // 最高字节
    r = ((dtext->color >> 16) & 0xFF); // 次高字节
    g = ((dtext->color >> 8) & 0xFF);  // 次低字节
    b = ((dtext->color >> 0) & 0xFF);  // 最低字节

    cairo_set_source_rgba(cr,
                          r / 255.0,
                          g / 255.0,
                          b / 255.0,
                          a / 255.0);
    pango_cairo_show_layout(cr, layout);

    // 清理
    g_object_unref(layout);
    cairo_destroy(cr);
    cairo_surface_destroy(surface);

    return GST_FLOW_OK;
}

static void gst_dynamic_text_class_init(GstDynamicTextClass *klass)
{
    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
    GstVideoFilterClass *vfilter_class = GST_VIDEO_FILTER_CLASS(klass);

    gobject_class->set_property = gst_dynamic_text_set_property;
    gobject_class->get_property = gst_dynamic_text_get_property;
    gobject_class->finalize = gst_dynamic_text_finalize;

    vfilter_class->transform_frame = gst_dynamic_text_transform_frame;

    g_object_class_install_property(gobject_class, PROP_TEXT,
                                    g_param_spec_string("text", "Text",
                                                        "Text to display",
                                                        "Hello World",
                                                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property(gobject_class, PROP_FONT_SIZE,
                                    g_param_spec_int("font-size", "Font Size",
                                                     "Font size in pixels",
                                                     1, 500, 24,
                                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property(gobject_class, PROP_FONT_NAME,
                                    g_param_spec_string("font-name", "Font Name",
                                                        "Font name to use",
                                                        "Sans",
                                                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property(gobject_class, PROP_X_POSITION,
                                    g_param_spec_double("x-position", "X Position",
                                                        "X position of the text",
                                                        0, G_MAXDOUBLE, 0,
                                                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property(gobject_class, PROP_Y_POSITION,
                                    g_param_spec_double("y-position", "Y Position",
                                                        "Y position of the text",
                                                        0, G_MAXDOUBLE, 0,
                                                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property(gobject_class, PROP_SCROLL_SPEED,
                                    g_param_spec_double("scroll-speed", "Scroll Speed",
                                                        "Speed of text scrolling",
                                                        0, G_MAXDOUBLE, 1.0,
                                                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property(gobject_class, PROP_SCROLL_DIRECTION,
                                    g_param_spec_int("scroll-direction", "Scroll Direction",
                                                     "Direction of text scrolling (0: horizontal, 1: vertical)",
                                                     0, 1, 0,
                                                     G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property(gobject_class, PROP_COLOR,
                                    g_param_spec_uint("color", "Color",
                                                      "Color to use for text (big-endian ARGB)",
                                                      0, G_MAXUINT32, 0xFFFFFFFF,
                                                      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    gst_element_class_set_static_metadata(GST_ELEMENT_CLASS(klass),
                                          "Dynamic Text Overlay",
                                          "Filter/Editor/Video",
                                          "Adds dynamic scrolling text overlay to video",
                                          "Thammer <tang_m_cong@163.com>");

    gst_element_class_add_static_pad_template(GST_ELEMENT_CLASS(klass),
                                              &sink_template);
    gst_element_class_add_static_pad_template(GST_ELEMENT_CLASS(klass),
                                              &src_template);
}

static void gst_dynamic_text_init(GstDynamicText *filter)
{
    filter->text = g_strdup("");
    filter->font_size = 24;
    filter->font_name = g_strdup("Sans");
    filter->x_position = 0;
    filter->y_position = 0;
    filter->scroll_speed = 1.0;
    filter->scroll_direction = 0;
    filter->color = 0xFFFFFFFF;
}

static gboolean plugin_init(GstPlugin *plugin)
{
    GST_DEBUG_CATEGORY_INIT(gst_dynamic_text_debug, "dynamictextoverlay", 0,
                            "Dynamic Text Overlay");

    return gst_element_register(plugin, "dynamictextoverlay", GST_RANK_NONE,
                                GST_TYPE_DYNAMIC_TEXT);
}

GST_PLUGIN_DEFINE(
        GST_VERSION_MAJOR,
        GST_VERSION_MINOR,
        dynamictextoverlay,
        "Dynamic Text Overlay Plugin",
        plugin_init,
        VERSION,
        "LGPL",
        "GStreamer",
        "http://gstreamer.net/"
    )